<?php

/*
    Language class
    
    Author:     Alfredo Mungo
    License:    LGPL
    Copyright:  2012
*/

/*
    Language class.
    
    
    This class is designed to add multilanguage support.
    
    You can register one or more languages and provide the instance with a .lang file which contains
        the messages you want to show in different languages. Each .lang file for the same target
        should contain the exact number of entries though it is not compulsory.
        
    Example .lang file: "
        # Webpage section data
        ABOUT_TITLE About
        ABOUT_SUBTITLE Want to know more?
        ABOUT_BIOGRAPHY Read my biography
        ABOUT_MORESKILLS Know more about my skills
        ABOUT_KNOWMORE Want to know more? Feel free to send me an email.

        PROJ_TITLE Projects
        PROJ_SUBTITLE See what I am working on
        PROJ_NONE No project available at the moment.

        DOCS_TITLE Documents
        DOCS_SUBTITLE See the topics I write about
        DOCS_NONE No documents available at the moment.

        # Common data names
        DATA_NAME Name
        DATA_AGE Age
        DATA_LOCATION Location
        DATA_UNIVERSITY University
        DATA_FACULTY Faculty
        DATA_JOB Job
        DATA_VERSION Version
        DATA_UPDATED Last updated
        DATA_CATEGORY Category

        # Common used words
        WORD_PROGRAMMER Computer Programmer
        WORD_BIOENG Biomedical Engineering

        # Hints
        HINT_READMORE Read more
        HINT_VIEWINGMAPS View in Google Maps
        HINT_CONTACTMEEMAIL Contact me via e-mail
    "
*/
class Language implements ArrayAccess {
    
    private $files;
    private $langs;
    private static $lang_result = null; // Language result
    
    function __construct() {
        $this->files = array();
        $this->langs = array();
    }
    
    /*
        Add a language to the languages list.
        
        Prototypes:
            public void add($name, $file)
            public void add($name, $lang)
        
        Arguments:
            mixed $name: The name of the language
            string $file: The filename (relative to PHI_ROOTDIR) of the language to be loaded (e.g.: '/langs/english.lang')
            array[string] $lang: An array containing in-source definitions for a language. Each key is a string representing the name of the entry, each value represents the entry text itself.
    */
    public function add($name, $file) {
    
        if(is_string($file))
            $this->files[$name] = $file;
        elseif(is_array($file))
            $this->langs[$name] = $file;
        else
            throw new WrongTypeException();
    }
    
    /*
        Load the language from disk.
        
        Return:
            True: language loaded.
            False: language not loaded.
    */
    private function load($name) {
        if(isset($this->files[$name])) { // Language found
            $path = PHI_ROOTDIR . $this->files[$name];
            $file = file($path, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
            $lang = array();
            
            if($file !== false) { // File found
                foreach($file as $line) {
                    $line = trim($line);
                    if(substr($line, 0, 1) != '#') { // Not a comment
                        $msg = explode(' ', $line, 2);
                        
                        if(count($msg) == 2) // Split operation completed succesfully
                            $lang[$msg[0]] = $msg[1];
                    }
                }
                
                $this->langs[$name] = $lang;
                return true;
            }
            else return false;
        }
        else return false;
    }
    
    public function offsetExists($offset) {
        if(!isset($this->langs[$offset]))
            if(!$this->load($offset))
                return false;
        
        return true;
    }
    
    public function offsetGet($offset) {
        if(!isset($this->langs[$offset]))
            if(!$this->load($offset))
                throw new IndexOutOfBoundException();

        return $this->langs[$offset];
    }
    
    public function offsetSet($offset, $value) {
        if(is_string($value))
            $this->langs[$offset] = $value;
        else
            throw new WrongTypeException();
    }
    
    public function offsetUnset($offset) {
        if(isset($this->langs[$offset]))
            unset($this->langs[$offset]);
    }
    
    // Static methods
    
    /*
        Guess the user's languages (from the Accept-Language header).
        
        Arguments:
            string $default_lang: the language ID to return if the language cannot be guessed (can be virtually any string).
        
        Return value:
            If the language is guessed, an array containing a list of language IDs (sorted by quality value) is returned.
            If the language cannot be guessed, an array containing only $default_lang is returned.
            
        Notes:
            ISO 3166-1-alpha-2 (country codes) http://www.iso.org/iso/prods-services/iso3166ma/02iso-3166-code-lists/country_names_and_code_elements
            ISO 639-1 (language codes) http://www.loc.gov/standards/iso639-2/php/English_list.php
            A language ID is defined as an ISO 639-1 code that CAN BE followed by a dash '-' and an ISO 3166 country code.
            This is an example list of valid language IDs: (en, en-US, en-GB, it, it-IT).
    */
    public static function guess($default_lang = PHI_LANG_DEFAULT) {
        if(self::$lang_result === null) {
            if(isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { // Can we try to guess the language from the HTTP headers?
                $langs = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']); // Subdivide each language
                $lang = array();
            
                foreach($langs as $lng) { // Set the quality value for each language
                    $slang = explode(';', $lng);
                
                    $slang[0] = trim($slang[0]);
                
                    if(count($slang) === 1) // Set default quality
                        $q = 1.0;
                    elseif(count($slang) >= 2) { // Retrieve quality value and assign it
                        $slang[1] = trim($slang[1]);
                    
                        if(substr($slang[1], 0, 2) == 'q=') // Is the associated information the quality? Yes
                            $q = (float) substr($slang[1], 2);
                        else // No, set it to default
                            $q = 1.0;
                    }
                
                    $rlang = array($slang[0], $q);
                    array_push($lang, $rlang);
                }
            
                // Sort the array by quality value
                do {
                    $swaps = 0;
                    $c = count($lang);
                
                    for($i = 0; $i < $c - 1; $i++) {
                        $l1 = $lang[$i];
                        $l2 = $lang[$i + 1];
                    
                        $q1 = $l1[1];
                        $q2 = $l2[1];
                    
                        if($q1 < $q2) { // Swap needed
                            $swaps++;
                        
                            $lang[$i] = $l2;
                            $lang[$i + 1] = $l1;
                        }
                    }
                } while($swaps > 0);
            
                // Compose the result array
                $result = array();
            
                foreach($lang as $lng)
                    array_push($result, $lng[0]);
            } else {
                $result = array($default_lang);
            }
            
            self::$lang_result = $result;
        }
        else
            $result = self::$lang_result;
            
        return $result;
    }
}

?>
